// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.


/****************************************************************************
						Microsoft RPC
          
                      picklp Example

    FILE:       picklpc.c

    USAGE:      picklpc  -f filename  (file to write to/read from)
                         -d           (serialization direction: decode)
                         -e           (serialization direction: encode)
                         -i           (incremental serialization on)

    PURPOSE:    The only side of RPC application

    FUNCTIONS:  main() - serializes data to or from a file

    COMMENTS:   Data procedure serialization is demonstrated here

****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include "picklp.h"    // header file generated by MIDL compiler

#define PURPOSE \
"This Microsoft RPC Version 2.0 sample program demonstrates\n\
the use of the [encode,decode] attributes. For more information\n\
about the attributes and RPC API functions, see the RPC programming\n\
guide and reference.\n\n"

/*  Used for incremental style only. */

void __RPC_USER PicAlloc( void * pState, char ** ppBuf, unsigned int * pCount);
void __RPC_USER PicWrite( void * pState, char *  pBuf,  unsigned int Count);
void __RPC_USER PicRead ( void * pState, char ** pBuf,  unsigned int * pCount);

typedef struct PickleControlBlock
{
    unsigned char *         pMemBuffer;
    unsigned char *         pBufferStart;
    unsigned long           LastSize;
} PickleControlBlock;

static PickleControlBlock                 UserState;
static PickleControlBlock * pUserState = &UserState;

void Usage(char * pszProgramName)
{
    fprintf_s(stderr, "%s", PURPOSE);
    fprintf_s(stderr, "Usage:  %s\n", pszProgramName);
    fprintf_s(stderr, " -d\n");
    fprintf_s(stderr, " -e\n");
    fprintf_s(stderr, " -ffilename\n");
    fprintf_s(stderr, " -i \n");
    exit(1);
}

void DumpData(
    char * pszComment,
    char * pszText,
    OBJECT1 * pObject1,
    OBJECT2 * pObject2 )
{
    int i;

    printf_s( "\n%s\n", pszComment );
    printf_s( "\tString\n\t\t%s\n", pszText );

    printf_s( "\tObject1");
    for (i=0; i < ARR_SIZE; i++)
        {
        if ( (i % 5) == 0 )
            printf_s( "\n\t\t");
        printf_s( "%08xl  ", pObject1->al[i] );
        }
    printf_s( "\n\t\t%x\n", pObject1->s );

    printf_s( "\tObject2");
    printf_s( "\n\t\t%x", pObject2->sSize );
    for (i=0; i < ARR_SIZE; i++)
        {
        if ( (i % 10) == 0 )
            printf_s( "\n\t\t");
        printf_s( "%04x  ", pObject2->as[i] );
        }
    printf_s( "\n" );
}

void WriteDataToFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulSizeToWrite )
{
    FILE *      pFile;
    size_t      Count;

    if ( pszFileName ) {

        fopen_s(&pFile,pszFileName, "w+b" );
        if ( pFile == NULL ) {
            printf_s("Cannot open the file for writing\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fwrite( &ulSizeToWrite, sizeof(byte), Count, pFile) != Count ) {
            printf_s("Cannot write1 to the file\n");
            exit(1);
            }

        Count = (size_t) ulSizeToWrite;
        if ( fwrite( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf_s("Cannot write2 to the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf_s("Failed to close the file\n");
            exit(1);
            }
        }
}

void ReadDataFromFile(
    char *          pszFileName,
    char *          pbBuffer,
    unsigned long   ulBufferSize )
{
    FILE *          pFile;
    size_t          Count;
    unsigned long   ulWrittenSize;


    if ( pszFileName ) {

       fopen_s(&pFile, pszFileName, "r+b" );
        if ( pFile == NULL ) {
            printf_s("Cannot open the file for reading\n");
            exit(1);
            }

        Count = sizeof(long);
        if ( fread( &ulWrittenSize, sizeof(byte), Count, pFile) != Count ) {
            printf_s("Cannot read1 from the file\n");
            exit(1);
            }

        Count = (size_t)ulWrittenSize;
        if ( fread( pbBuffer, sizeof(byte), Count, pFile) != Count ) {
            printf_s("Cannot read2 from the file\n");
            exit(1);
            }

        if ( fclose( pFile ) != 0) {
            printf_s("Failed to close the file\n");
            exit(1);
            }
        }
}

void __cdecl main(int argc, char **argv)
{
    RPC_STATUS status;

    unsigned char * pbPicklingBuffer = NULL;

    char * pszStyle      = NULL;
    char * pszFileName   = "pickle.dat";
    int i;
    int fEncode = 1;
    int fFixedStyle = 1;

    /* allow the user to override settings with command line switches */
    for (i = 1; i < argc; i++) {
        if ((*argv[i] == '-') || (*argv[i] == '/')) {
            switch (tolower(*(argv[i]+1))) {
            case 'd':
                fEncode = 0;
                break;
            case 'e':
                fEncode = 1;
                break;
            case 'i':
                fFixedStyle = 0;
                break;
            case 'f':
                pszFileName = argv[i] + 2;
                break;
            case 'h':
            case '?':
            default:
                Usage(argv[0]);
            }
        }
        else
            Usage(argv[0]);
    }

    /* Fixed buffer style: the buffer should be big enough.  */
    /* Please note that the buffer has to be aligned at 8.   */

    pbPicklingBuffer = (unsigned char *)
            midl_user_allocate( BUFSIZE * sizeof(unsigned char));

    if ( pbPicklingBuffer == NULL ) {
        printf_s("Cannot allocate the pickling buffer\n");
        exit(1);
        }
    else
        memset( pbPicklingBuffer, 0xdd, BUFSIZE );

    /*
        Set the pickling handle that will be used for data serialization.
        The global ImplicitPicHandle is used, but it has to be set up.
    */

    if ( fEncode ) {

        unsigned char * pszNameId;
        OBJECT1         Object1;
        OBJECT2   *     pObject2;
        unsigned long   ulEncodedSize = 0;

        printf_s("\nEncoding run: use -d for decoding\n\n");

        if ( fFixedStyle ) {

            printf_s("Creating a fixed buffer encoding handle\n");
            status = MesEncodeFixedBufferHandleCreate( pbPicklingBuffer,
                                                       BUFSIZE,
                                                       & ulEncodedSize,
                                                       & ImplicitPicHandle );
            printf_s("MesEncodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf_s("Creating an incremental encoding handle\n");
            status = MesEncodeIncrementalHandleCreate( pUserState,
                                                       PicAlloc,
                                                       PicWrite,
                                                       & ImplicitPicHandle );
            printf_s("MesEncodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }

        /* Creating objects to manipulate */

        pszNameId = "Procedure pickling sample";

        for (i = 0; i < ARR_SIZE; i++)
            Object1.al[i] = 0x37370000 + i;
        Object1.s = 0x4646;

        pObject2 = midl_user_allocate( sizeof(OBJECT2) + ARR_SIZE*sizeof(short) );
        if (pObject2 == NULL ) {
            printf_s("Out of memory for Object2\n");
            exit(1);
        }

        pObject2->sSize = ARR_SIZE;
        for (i = 0; i < ARR_SIZE; i++)
            pObject2->as[i] = 0x7700 + i;

        DumpData( "Data to be encoded", pszNameId, &Object1, pObject2 );

        printf_s("\nEncoding all the arguments to the buffer\n\n");
        ProcPickle( pszNameId, & Object1, pObject2 );

        printf_s("Writing the data to the file: %s\n", pszFileName);
        WriteDataToFile( pszFileName,
                         pbPicklingBuffer,
                         fFixedStyle  ? ulEncodedSize
                                      : pUserState->LastSize);

        midl_user_free( pObject2 );
    }
    else {
        char            acNameBuffer[50];
        OBJECT1         Object1;
        OBJECT2   *     pObject2;

        printf_s("\nDecoding run: use -e for encoding\n\n");

        printf_s("Reading the data from the file: %s\n\n", pszFileName );
        ReadDataFromFile( pszFileName,
                          pbPicklingBuffer,
                          BUFSIZE );

        if ( fFixedStyle ) {

            printf_s("Creating a decoding handle\n");
            status = MesDecodeBufferHandleCreate( pbPicklingBuffer,
                                                  BUFSIZE,
                                                  & ImplicitPicHandle );
            printf_s("MesDecodeFixedBufferHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }
        else {

            pUserState->LastSize = 0;
            pUserState->pMemBuffer = (char *)pbPicklingBuffer;
            pUserState->pBufferStart = (char *)pbPicklingBuffer;

            printf_s("Creating an incremental decoding handle\n");
            status = MesDecodeIncrementalHandleCreate( pUserState,
                                                       PicRead,
                                                       & ImplicitPicHandle );
            printf_s("MesDecodeIncrementalHandleCreate returned 0x%x\n", status);
            if (status) {
                exit(status);
            }
        }


        /* Creating objects to manipulate */

        pObject2 = midl_user_allocate( sizeof(OBJECT2) + ARR_SIZE*sizeof(short));
        if (pObject2 == NULL ) {
            printf_s("Out of memory for Object2\n");
            exit(1);
        }

        printf_s("\nDecoding all the arguments from the buffer\n");
        ProcPickle( acNameBuffer, & Object1, pObject2 );

        DumpData( "Decoded data", acNameBuffer, &Object1, pObject2 );

        midl_user_free( pObject2 );
    }

    printf_s("\nData serialization done.\n");

    midl_user_free( pbPicklingBuffer );

    printf_s("\nFreeing the serialization handle.\n");
    status = MesHandleFree( ImplicitPicHandle );
    printf_s("MesHandleFree returned 0x%x\n", status);

    exit(0);

}  // end main()

/*********************************************************************/
/*                 MIDL allocate and free                            */
/*********************************************************************/

/* A pickling buffer has to be aligned at 8.
   malloc() doesn't guarantee that, so some gimmick has to be done.
*/

#define ALIGN_TO8( p)   (char *)((ULONG_PTR)((char *)p + 7) & ~7)

void __RPC_FAR * __RPC_USER  MIDL_user_allocate(size_t s)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcAllocated = (unsigned char *) malloc( s + 15 );
    pcUserPtr =  ALIGN_TO8( pcAllocated );
    if ( pcUserPtr == pcAllocated )
        pcUserPtr = pcAllocated + 8;

    *(pcUserPtr - 1) = (char) (pcUserPtr - pcAllocated);

    return( pcUserPtr );
}

void __RPC_USER  MIDL_user_free(void __RPC_FAR *f)
{
    unsigned char * pcAllocated;
    unsigned char * pcUserPtr;

    pcUserPtr = (unsigned char *) f;
    pcAllocated = pcUserPtr - *(pcUserPtr - 1);

    free( pcAllocated );
}

/*********************************************************************/
/*                 Incremental style helper routines                 */
/*********************************************************************/

void  __RPC_USER
PicAlloc(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routines "allocates" the next part from the preallocated
       buffer. It could call midl_user_allocate, too.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    if ( (pPic->pMemBuffer - pPic->pBufferStart) + *pCount <= BUFSIZE )
        *ppBuf = (char *)pPic->pMemBuffer;
    else {
        printf_s(" Buffer too small\n");
        exit(1);
    }
}

void __RPC_USER
PicWrite(
    void *          pState,
    char *          pBuf,
    unsigned int    Count )
{
    /* This routine just marks how much of the preallocated buffer is used.
    */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;


    if ( pPic->pMemBuffer != pBuf ) {
        printf_s(" Buffer poiner corrupted\n");
        exit(1);
    }
    pPic->pMemBuffer += Count;
    pPic->LastSize += Count;
}

void __RPC_USER
PicRead(
    void *          pState,
    char **         ppBuf,
    unsigned int  * pCount )
{
    /* This routine returns another portion of the preread buffer. */

    PickleControlBlock * pPic = (PickleControlBlock *)pState;

    *ppBuf = (char *)pPic->pMemBuffer;
    pPic->pMemBuffer += *pCount;
}

/* end picklpc.c */


